// PVS 3/6/2008 - added to validate addresses with UPS Online Tools

using System;
using System.Collections.Specialized;
using System.Data;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Configuration.Provider;
using System.Web.Configuration;
using Commerce.Common;

namespace Commerce.Providers
{
    public class UpsAddressValidateProvider :  ProviderBase
    {
        string _avconnectionUrl = "";
        string _upsAccessKey = "";
        string _upsUserName = "";
        string _upsPassword = "";

        #region Provider initialize

        public void InitProvider(string name, NameValueCollection config)
        {
            if (config == null)
                throw new ArgumentNullException("config");

            base.Initialize(name, config);

            // PVS 3/6/2008 - for address validation
            _avconnectionUrl = config["avconnectionUrl"].ToString();
            if (String.IsNullOrEmpty(_avconnectionUrl))
                throw new ProviderException("Empty or missing AVconnectionUrl");
            config.Remove("avconnectionUrl");

            _upsAccessKey = config["upsAccessKey"].ToString();
            if (String.IsNullOrEmpty(_upsAccessKey))
                throw new ProviderException("Empty or missing upsAccessKey");
            config.Remove("upsAccessKey");

            _upsUserName = config["upsUserName"].ToString();
            if (String.IsNullOrEmpty(_upsUserName))
                throw new ProviderException("Empty or missing upsUserName");
            config.Remove("upsUserName");

            _upsPassword = config["upsPassword"].ToString();
            if (String.IsNullOrEmpty(_upsPassword))
                throw new ProviderException("Empty or missing upsPassword");
            config.Remove("upsPassword");

            //Throw an exception if unrecognized attributes remain
            if (config.Count > 0)
            {
                string attr = config.GetKey(0);
                if (!String.IsNullOrEmpty(attr))
                    throw new ProviderException
                        ("Unrecognized attribute: " + attr);
            }
        }

        #endregion

        #region Constructors

        public UpsAddressValidateProvider()
		{
            AVServiceSection config = new AVServiceSection();
            config = (AVServiceSection)
               System.Configuration.ConfigurationManager.GetSection("AVService");

            _avconnectionUrl = config.Providers["UpsAddressValidateProvider"].Parameters["avconnectionUrl"].ToString();
            _upsAccessKey = config.Providers["UpsAddressValidateProvider"].Parameters["upsAccessKey"].ToString();
            _upsUserName = config.Providers["UpsAddressValidateProvider"].Parameters["upsUserName"].ToString();
            _upsPassword = config.Providers["UpsAddressValidateProvider"].Parameters["upsPassword"].ToString();

		}

		#endregion

        #region Methods

        public AddressValidateCollection CheckAddress(Address address)
        {
            HttpRequestHandler http = new HttpRequestHandler(_avconnectionUrl);
            string validate = http.POST(validateRequest(address));
            AddressValidateCollection collection = ParseResponse(validate);

            return collection;
        }

        private string validateRequest(Address address)
        {
            StringBuilder sb = new StringBuilder();
            //Build Access Request
            sb.Append("<?xml version='1.0'?>");
            sb.Append("<AccessRequest xml:lang='en-US'>");
            sb.AppendFormat("<AccessLicenseNumber>{0}</AccessLicenseNumber>", _upsAccessKey);
            sb.AppendFormat("<UserId>{0}</UserId>", _upsUserName);
            sb.AppendFormat("<Password>{0}</Password>", _upsPassword);
            sb.Append("</AccessRequest>");
            //Build AV Request
            sb.Append("<?xml version='1.0'?>");
            sb.Append("<AddressValidationRequest xml:lang='en-US'>");
            sb.Append("<Request>");
            sb.Append("<TransactionReference>");
            sb.AppendFormat("<CustomerContext>{0}</CustomerContext>", address.LastName);
            sb.Append("<XpciVersion>1.0001</XpciVersion>");
            sb.Append("</TransactionReference>");
            sb.Append("<RequestAction>AV</RequestAction>");
            sb.Append("</Request>");
            sb.Append("<Address>");
            sb.AppendFormat("<City>{0}</City>",address.City);
            sb.AppendFormat("<StateProvinceCode>{0}</StateProvinceCode>",address.StateOrRegion);
            sb.Append("</Address>");
            sb.Append("</AddressValidationRequest>");

            return sb.ToString();
        }

        private AddressValidateCollection ParseResponse(string response)
        {
            AddressValidateCollection optionCollection = new AddressValidateCollection();
            StringReader sr = new StringReader(response);
            XmlTextReader tr = new XmlTextReader(sr);
            try
            {
                while (tr.Read())
                {
                    // this is the top level of a response
                    if ((tr.Name == "AddressValidationResponse") && (tr.NodeType == XmlNodeType.Element))
                    {
                        int statusCode = 1;  // assume success
                        string statusDesc = "";

                        int rank = 0;
                        decimal quality = 0;
                        string city = "";
                        string state = "";
                        string zipLowEnd = "";
                        string zipHighEnd = "";

                        while (tr.Read())
                        {
                            if ((tr.Name == "ResponseStatusCode") && (tr.NodeType == XmlNodeType.Element))
                            {
                                statusCode = int.Parse(tr.ReadString());
                                tr.ReadEndElement();
                            }
                            if ((tr.Name == "ResponseStatusDescription") && (tr.NodeType == XmlNodeType.Element))
                            {
                                statusDesc = tr.ReadString();
                                tr.ReadEndElement();
                            }

                            // check for error status
                            if (statusCode != 1)
                            {
                                throw new ProviderException("UPS Error returned: " + statusDesc);
                            }

                            // this is the start of the return data
                            if ((tr.Name == "AddressValidationResult") && (tr.NodeType == XmlNodeType.Element))
                            {
                                while (tr.Read())
                                {
                                    if ((tr.Name == "Rank") && (tr.NodeType == XmlNodeType.Element))
                                    {
                                        rank = int.Parse(tr.ReadString());
                                        tr.ReadEndElement();
                                    }

                                    if ((tr.Name == "Quality") && (tr.NodeType == XmlNodeType.Element))
                                    {
                                        quality = Convert.ToDecimal(tr.ReadString());
                                        tr.ReadEndElement();
                                    }

                                    if ((tr.Name == "Address") && (tr.NodeType == XmlNodeType.Element))
                                    {
                                        while (tr.Read())
                                        {
                                            if ((tr.Name == "City") && (tr.NodeType == XmlNodeType.Element))
                                            {
                                                city = tr.ReadString();
                                                tr.ReadEndElement();
                                            }
                                            
                                            if ((tr.Name == "StateProvinceCode") && (tr.NodeType == XmlNodeType.Element))
                                            {
                                                state = tr.ReadString();
                                                tr.ReadEndElement();
                                            }

                                            if ((tr.Name == "Address") && (tr.NodeType == XmlNodeType.EndElement))
                                            {
                                                break;
                                            }
                                        }
                                    }
                                    if ((tr.Name == "PostalCodeLowEnd") && (tr.NodeType == XmlNodeType.Element))
                                    {
                                        zipLowEnd = tr.ReadString();
                                        tr.ReadEndElement();
                                    }

                                    if ((tr.Name == "PostalCodeHighEnd") && (tr.NodeType == XmlNodeType.Element))
                                    {
                                        zipHighEnd = tr.ReadString();
                                        tr.ReadEndElement();
                                    }
                                
                                //end of AVR
                                if ((tr.Name == "AddressValidationResult") && (tr.NodeType == XmlNodeType.EndElement))
                                {
                                    break;
                                }
                            }
                                // create the AddressValidate list item
                                AddressValidate option = new AddressValidate();
                                option.rank = rank;
                                option.quality = quality;
                                option.city = city;
                                option.state = state;
                                option.zipLow = zipLowEnd;
                                option.zipHigh = zipHighEnd;
                                option.returnDisplay = city + ", " + state + " - ZIP Range: " + zipLowEnd + " - " + zipHighEnd;


                                optionCollection.Add(option);

                            }
                        }

                        

                    }
                }


                sr.Dispose();

                return optionCollection;

            }
            catch
            {
                throw;
            }
            finally
            {
                sr.Close();
                tr.Close();
            }
        }

        #endregion

    }
}

